﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;
using NPOI.HSSF.UserModel;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;
using System.Threading;

namespace WebForm
{
    public partial class CustomControl : UserControl
    {
        public int FromId { get; set; }
        public int ToId { get; set; }
        public int CurrentProcessId;
        public float Delay { get; set; }
        private List<PeakbaggerMountain> notFoundIds;

        private object m_locker = new object();
        public CustomControl()
        {
            InitializeComponent();
            notFoundIds = new List<PeakbaggerMountain>();
            Delay = 2;
            webBrowser1.ScriptErrorsSuppressed = true;
            m_bgWorker = new BackgroundWorker();
            m_bgWorker.WorkerSupportsCancellation = true;
            m_bgWorker.DoWork += m_bgWorker_DoWork;
            m_bgWorker.ProgressChanged += m_bgWorker_ProgressChanged;
            m_bgWorker.RunWorkerCompleted += m_bgWorker_RunWorkerCompleted;
            m_bgWorker.WorkerReportsProgress = true;
        }

        void m_bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            SaveData();
            m_progressBar.Value = 100;
            m_lblStatus.Text = "Done!";
        }

        void m_bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            m_progressBar.Value = e.ProgressPercentage;
            m_lblCurrentId.Text = CurrentProcessId.ToString();
        }

        void m_bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker = sender as BackgroundWorker;

            string url;

            for (int i = FromId; i <= ToId; i++) {
                CurrentProcessId = i;
                if ((worker.CancellationPending == true)) {
                    FromId = CurrentProcessId;
                    e.Cancel = true;
                    break;
                }
                url = "http://www.peakbagger.com/peak.aspx?pid=" + i;
                this.Invoke((Action)delegate() {
                    m_lblStatus.Text = "Working......";
                    webBrowser1.Navigate(url);
                });

                string content = string.Empty;
                bool isTimeout = false;
                webBrowser1.Invoke((Action)delegate()
                {
                    DateTime processTime = DateTime.Now;
                    while (webBrowser1.ReadyState != WebBrowserReadyState.Complete)
                    {
                        Application.DoEvents();
                        if (DateTime.Now.Subtract(processTime).Minutes > 2)
                        {
                            isTimeout = true;
                            break;
                        }
                    }
                    if (!isTimeout)
                    {
                        content = webBrowser1.DocumentText;
                    }
                });
                if (isTimeout)
                {
                    i--;
                    continue;
                }
                if (!(content.Contains("No record found for") || content.Contains("The webpage cannot be found") || content.Length <= 200))
                {
                    var info = GetMountInformation(content);
                    if (info != null)
                    {
                        lock (m_locker)
                        {
                            info.Id = i;
                            notFoundIds.Add(info);
                        }
                    }
                }
                this.BeginInvoke((Action)delegate()
                {
                    m_lblStatus.Text = "Sleeping......";
                });
                worker.ReportProgress(((i -FromId) * 100) / ((ToId - FromId) == 0 ? 1 : ToId - FromId));
                if (i == ToId)
                {
                    break;
                }
                Thread.Sleep(TimeSpan.FromSeconds(Convert.ToDouble(Delay)));
            }
        }
        private void SaveData()
        {
            if (notFoundIds.Count <= 0)
            {
                return;
            }
            var workbook = new HSSFWorkbook();
            var sheet = workbook.CreateSheet("Mountains");
            var headerRow = sheet.CreateRow(0);
            headerRow.CreateCell(0).SetCellValue("Id");
            headerRow.CreateCell(1).SetCellValue("Name");
            headerRow.CreateCell(2).SetCellValue("Prominence (in meter)");
            headerRow.CreateCell(3).SetCellValue("Elevation (in meter)");
            headerRow.CreateCell(4).SetCellValue("Latitude/Longitude (WGS84)");
            headerRow.CreateCell(7).SetCellValue("Country");
            headerRow.CreateCell(8).SetCellValue("State");
            headerRow.CreateCell(9).SetCellValue("Ownership");
            NPOI.SS.Util.CellRangeAddress cra = new NPOI.SS.Util.CellRangeAddress(0, 0, 4, 6);
            sheet.AddMergedRegion(cra);
            lock (m_locker)
            {
                for (int i = 0; i < notFoundIds.Count; i++)
                {
                    var company = notFoundIds[i];
                    var row = sheet.CreateRow(i + 1);
                    row.CreateCell(0).SetCellValue(company.Id.ToString());
                    row.CreateCell(1).SetCellValue(company.Name);
                    row.CreateCell(2).SetCellValue(company.Prominence);
                    row.CreateCell(3).SetCellValue(company.Elevation);
                    row.CreateCell(4).SetCellValue(company.LongLat1);
                    row.CreateCell(5).SetCellValue(company.LongLat2);
                    row.CreateCell(6).SetCellValue(company.LongLat3);
                    row.CreateCell(7).SetCellValue(company.Country);
                    row.CreateCell(8).SetCellValue(company.State);
                    row.CreateCell(9).SetCellValue(company.Ownership);
                }
                notFoundIds.Clear();
            }
            try
            {
                string fileName = string.Format("peakbagger_ouput_{0}_{1}.xls", FromId, CurrentProcessId);
                int index = 1;
                while (File.Exists(fileName))
                {
                    fileName = string.Format("peakbagger_ouput_{0}_{1}_({2}).xls", FromId, CurrentProcessId, index++);
                }
                using (var file = new FileStream(fileName, FileMode.Create))
                {
                    workbook.Write(file);
                    file.Flush();
                    file.Close();
                }
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.Message, "Error!");
            }


            MessageBox.Show("Finish!");
        }
        private PeakbaggerMountain GetMountInformation(string document)
        {
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(document);
            try
            {
                var name = doc.DocumentNode.SelectSingleNode("/html/body//table[3]/tr/td//h1");
                if (name != null)
                {
                    PeakbaggerMountain result = new PeakbaggerMountain();
                    result.Name = name.InnerText;
                    var prome = doc.DocumentNode.SelectSingleNode("/html/body//table[3]/tr/td/table[1]/tr/td");
                    if (prome != null)
                    {
                        result.Prominence = GetMeterFromStr(prome.InnerText).Trim();
                    }
                    var elavator =
                        doc.DocumentNode.SelectSingleNode("/html/body//table[3]/tr/td/table[1]/tr//h2");
                    if (elavator != null)
                    {
                        result.Elevation = GetMeterFromStr(elavator.InnerText);
                    }
                    var logLatTable = doc.DocumentNode.SelectSingleNode("/html/body//table[3]/tr/td/table[2]");
                    if (logLatTable != null)
                    {
                        var longLatnode = logLatTable.SelectSingleNode(".//td[contains(text(),'Latitude/Longitude')]");
                        if (longLatnode != null)
                        {
                            var longlatVar = longLatnode.NextSibling;
                            List<string> lat = GetLines(longlatVar);
                            if (lat.Count > 0)
                            {
                                result.LongLat1 = lat[0].Trim();
                            }
                            if (lat.Count > 1)
                            {
                                result.LongLat2 = lat[1].Trim();
                            }
                            if (lat.Count > 2)
                            {
                                result.LongLat3 = lat[2].Trim();
                            }
                        }
                        var country = logLatTable.SelectSingleNode(".//td[contains(text(),'Country')]");
                        if (country != null)
                        {
                            var cValue = country.NextSibling;
                            result.Country = cValue.InnerText.Trim();
                        }
                        var state = logLatTable.SelectSingleNode(".//td[contains(text(),'State/Province')]");
                        if (state != null)
                        {
                            var sValue = state.NextSibling;
                            result.State = sValue.InnerText.Trim();
                        }

                    }
                    var ownTable = doc.DocumentNode.SelectSingleNode("/html/body//table[3]/tr/td/table[3]");
                    if (ownTable != null)
                    {
                        var tdOwnerShip = ownTable.SelectSingleNode(".//td[contains(text(),'Ownership')]");
                        if (tdOwnerShip != null)
                        {
                            result.Ownership = tdOwnerShip.NextSibling.InnerText.Trim();
                        }
                    }
                    return result;
                }
            }
            catch (Exception ex)
            {

            }



            return null;
        }
        private List<string> GetLines(HtmlNode node)
        {
            var result = new List<string>();
            if (node != null)
            {
                StringBuilder builder = new StringBuilder();
                foreach (HtmlNode childNode in node.ChildNodes)
                {
                    if (childNode.Name == "br")
                    {
                        result.Add(builder.ToString().Trim());
                        builder = new StringBuilder();
                    }
                    builder.Append(childNode.InnerText);
                }
                result.Add(builder.ToString().Trim());
            }
            return result;
        }
        private string GetMeterFromStr(string value)
        {
            if (string.IsNullOrEmpty(value.Trim()))
            {
                return string.Empty;
            }
            value = value.Trim();
            var index = value.IndexOf(":");
            if (index > 0)
            {
                value = value.Substring(index + 1).Trim();
            }
            var mF = value.IndexOf("m,");
            if (mF < 0)
            {
                mF = value.IndexOf("s,");
            }
            if (mF > 0)
            {
                return value.Substring(0, mF + 1);
            }
            mF = value.IndexOf("t,");
            if (mF > 0)
            {
                return value.Substring(mF + 2);
            }
            return string.Empty;
        }
        private BackgroundWorker m_bgWorker;
        public void Stop()
        {
            m_bgWorker.CancelAsync();
            SaveData();
        }
        public void Run()
        {
            if (m_bgWorker.IsBusy != true)
            {
                m_bgWorker.RunWorkerAsync();
            }
        }
    }
}
